package com.zhang.authority.service.impl;

import com.zhang.authority.model.config.MutableUser;
import com.zhang.authority.model.config.MutableUserDetails;
import com.zhang.authority.model.config.Role;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.log.LogMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;

/**
 * UserDetailsService只有验证用户的方法
 * UserDetailsManager新增了增删改查
 * UserDetailsPasswordService提供了更新密码
 *
 * InitializingBean这个为了方便测试
 *
 * @author zhangFanJun
 * @date 2024-07-09 17:55
 **/
@RequiredArgsConstructor
@Slf4j
@Service
public class InMemoryUserDetailsService implements InitializingBean, UserDetailsManager, UserDetailsPasswordService {

    private final PasswordEncoder passwordEncoder;
    protected final Log logger = LogFactory.getLog(this.getClass());
    private final Map<String, MutableUserDetails> users = new HashMap();
    private  AuthenticationManager authenticationManager;


    /**
     * 初始化一个
     * */
    @Override
    public void afterPropertiesSet() throws Exception {

        UserDetails user = User.builder()
                .username("root")
                .password(passwordEncoder.encode("root"))
                .roles(Role.USER.getRoleName())
                .build();
        createUser(user);
        log.info("完成了用户的初始化:{}",user);
    }

    @Override
    public void createUser(UserDetails user) {

        Assert.isTrue(!userExists(user.getUsername()), "user should not exist");
        this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
    }

    @Override
    public void deleteUser(String username) {
        this.users.remove(username.toLowerCase());
    }

    @Override
    public void updateUser(UserDetails user) {
        Assert.isTrue(userExists(user.getUsername()), "user should exist");
        this.users.put(user.getUsername().toLowerCase(), new MutableUser(user));
    }

    @Override
    public boolean userExists(String username) {

        log.info("判断用户名是否存在：{}",username);
        return this.users.containsKey(username.toLowerCase());
    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {
        Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
        if (currentUser == null) {
            // This would indicate bad coding somewhere
            throw new AccessDeniedException(
                    "Can't change password as no Authentication object found in context " + "for current user.");
        }
        String username = currentUser.getName();
        this.logger.debug(LogMessage.format("Changing password for user '%s'", username));
        // If an authentication manager has been set, re-authenticate the user with the
        // supplied password.
        if (this.authenticationManager != null) {
            this.logger.debug(LogMessage.format("Reauthenticating user '%s' for password change request.", username));
            this.authenticationManager
                    .authenticate(UsernamePasswordAuthenticationToken.unauthenticated(username, oldPassword));
        }
        else {
            this.logger.debug("No authentication manager set. Password won't be re-checked.");
        }
        MutableUserDetails user = this.users.get(username);
        Assert.state(user != null, "Current user doesn't exist in database.");
        user.setPassword(newPassword);
    }

    @Override
    public UserDetails updatePassword(UserDetails user, String newPassword) {
        String username = user.getUsername();
        MutableUserDetails mutableUser = this.users.get(username.toLowerCase());
        mutableUser.setPassword(newPassword);
        return mutableUser;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        log.info("通过用户名查找用户：{}",username);
        UserDetails user = this.users.get(username.toLowerCase());
        if (user == null) {
            throw new UsernameNotFoundException(username);
        }
        return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),
                user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
    }

    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }


}
